package SequenceToFragments;

use Class::Struct;
use List::Util qw[sum];
use Math::Combinatorics;
use Storable qw[dclone];
use MassData;

## The purpose of the function is to calculate the mass loss of the terminal. 
sub EdgeCut {

	my $chain_ref = shift;
	our $dp = shift;
	my $candi_frags; # Ref to hash?
	my @trun_frags;
	our @links = @{$GAGInfo{'HS'}->{'link'}};
	our %MonoTable = %{$BasicInfo{'MonoTable'}};
	## Decide the position of NRE/RE cleavage, calculate the mass loss based on the cleavage type.
	## Choose the edge, and then map to the corresponding node. Left and right terminals are treated as edges with value of 0.
	my $count = 1;
	## Convert to node order: $i..$j-1 and then unit order
	for(my $nre=0; $nre<2*$dp-1; $nre++){ # NRE-Edges: 2 * ($dp-1) + 2 = 2*$dp 
	
		$candi_frags = UnitCleavage($chain_ref,$nre,-1); 

		for(my $re=$nre+2; $re<2*$dp; $re++){ # RE-Edges 
	
			foreach my $chain_frag (@{$candi_frags}){	
				
				my $complete_frags = UnitCleavage($chain_frag,$re,1); 
				
				foreach my $seg_frag (@{$complete_frags}) {
					
					push @trun_frags, [@{$seg_frag}[$nre..($re-1)]];
					#print "NRE:$nre\tRE:$re\tChain:$#trun_frags\t$count\n";
					#$count++;
				}
			}
		}	
	}
	return \@trun_frags;
	
}
sub InternalCut {
	my $seg_frag = shift;
	## Select qualified Id fo the fragment units.
	## Terminal should not be taken into account.
	my @tags = grep {$seg_frag->[$_]->idx%2==0 && $seg_frag->[$_]->fragment->cleavage->direction==0} 1..($#{$seg_frag}-1);
	my $internal_loss = '';
	my %seen = ();
	#if($InternalNum!=0 && @tags > 0) {
	if(@tags > 0) {
		for(my $i=0; $i<4; $i++){
			
			for(my $j=$i+2; $j<6; $j++){
			
				next if ($links[0]>$i && $links[1] > $j && $links[0] <= $j) || ($links[0] <= $i && $links[1] <= $j);
				next if ($i==0 && $j==5); # (0,5) cleavage is not OK.
				
				my @ms_ref;
				@ms_ref = (($i+1)..$j) if $links[0]>$i && $links[1]<=$j;
				@ms_ref = (0..$i, ($j+1)..5) if $links[0]<=$i && $links[1]>$j;
		
				foreach my $id (@tags) {
					my $mass = sum(@{$seg_frag->[$id]->fragment->piece}[@ms_ref]); 
					$seen{$mass}++;
				}
			}
		}		
		$internal_loss = join '-', keys(%seen);
	}
	return $internal_loss;	
}
sub UnitCleavage {
	
	my ($chain_ref,$index,$direction) = @_;
	
	my @group_frags;
	$index = $index-1 if $direction==1;
	## Complete fragment without terminal cleavage.
	#push @group_frags, dclone($chain_ref);
	
	#return \@group_frags if ($direction==-1 && $index%2==1)  ||  ($direction==1 && $index%2==0);

	if($index%2==1) {
		my $temp_chain = dclone($chain_ref);
		## Based on the knowledge of cleavage.
	    $temp_chain->[$index]->fragment->piece->[0] += $MonoTable{'H'};
		push @group_frags, $temp_chain;
		return \@group_frags;	
	} elsif($index%2==0 && $index!=0 && ($index!=2*$main::dp-2)) {
		my $temp_chain = dclone($chain_ref);
		if($direction==-1) {
			$temp_chain->[$index]->fragment->piece->[5] -= $MonoTable{'H'};
		} elsif($direction==1) {
			$temp_chain->[$index]->fragment->piece->[2] -= $MonoTable{'H'};
		}
		push @group_frags, $temp_chain;
		#return \@group_frags;
	}
	## Get the index of the node from that of edge.
	push @group_frags, dclone($chain_ref);
	#$index = $index-1 if $direction==1;

	for(my $i=0; $i<4; $i++){
		for(my $j=$i+2; $j<6; $j++){

			
			## Unreasonable cleavage!
			next if ($i==0 && $j==5);
			my $temp_chain = dclone($chain_ref);
			
			## Cleavage of double bond!!
			if($j==4 && $index==0) {
				$temp_chain->[0]->fragment->piece->[5] += $MonoTable{'H'};
			}

			## Update $temp_unit.
			$temp_chain->[$index]->fragment->cleavage->begin($i);
			$temp_chain->[$index]->fragment->cleavage->end($j);
			$temp_chain->[$index]->fragment->cleavage->direction($direction);
			
			my $ms_ref1 = [(0) x ($i+1), @{$temp_chain->[$index]->fragment->piece}[$i+1..$j], (0) x (5-$j)];
			my $ms_ref2 = [@{$temp_chain->[$index]->fragment->piece}[0..$i], (0) x ($j-$i), @{$temp_chain->[$index]->fragment->piece}[$j+1..5]];
			
			if($direction==-1){
				$temp_chain->[$index]->fragment->piece($links[0]>$i ? $ms_ref1 : $ms_ref2);
			} elsif($direction==1){
				$temp_chain->[$index]->fragment->piece($links[1]<=$j? $ms_ref1 : $ms_ref2);
			} else {
				die "The parameter for terminal direction is invalid!\n";
			}
			
			push @group_frags, $temp_chain;
		}
	}

	return \@group_frags;

}


1; 
